"use client";

import { useState, useRef, useEffect } from "react";
import Webcam from "react-webcam";
import { Pose } from "@mediapipe/pose";
import * as cam from "@mediapipe/camera_utils";
import * as mediapipePose from "@mediapipe/pose";
import { drawConnectors, drawLandmarks } from "@mediapipe/drawing_utils";
import toast, { Toaster } from "react-hot-toast";

import { Notifications } from "react-push-notification";
import addNotification from "react-push-notification";

import NotificationPing from "./../public/sounds/ping.mp3";
import logo from "./../public/logo.png";

export default function Home() {
  const [isRecording, setIsRecording] = useState(false);
  const [showDownload, setShowDownload] = useState(false);
  const [lastToastTime, setLastToastTime] = useState(0);

  const webcamRef = useRef(null);
  const canvasRef = useRef(null);

  const notifPing = useRef(null);

  const postureRef = useRef(null);
  const poseRef = useRef(null);
  const cameraRef = useRef(null);

  let goodPosture = null;
  let loaded = false;
  let badPostureCount = 0;

  const dataRef = useRef([]);
  const startTimeRef = useRef(null);
  const slouchIntervalRef = useRef(null);
  const lastSlouchStateRef = useRef(false); // keeps track of last posture state

  // Function to ensure notification permission is granted
  function ensureNotificationPermission() {
    if ("Notification" in window) {
      if (Notification.permission === "default") {
        Notification.requestPermission().then((permission) => {
          if (permission !== "granted") {
            console.log("Notification permission not granted by user.");
          } else {
            console.log("Notification permission granted.");
          }
        });
      } else if (Notification.permission === "denied") {
        console.log(
          "User has denied notification permission. Cannot show notifications."
        );
      } else {
        console.log("Notification permission already granted.");
      }
    } else {
      console.log("This browser does not support notifications.");
    }
  }

  useEffect(() => {
    // Request notification permission when component mounts
    ensureNotificationPermission();
  }, []);

  const PoseNotFound = ({ text, subtext }) => {
    console.log("Tryna send notification...");

    // Check if permission is granted before showing notification
    if (Notification.permission === "granted") {
      addNotification({
        title: text,
        message: subtext,
        native: true,
        icon: logo.src,
      });
    } else {
      console.log(
        "Cannot show notification because notification permission isn't granted."
      );
    }
  };

  useEffect(() => {
    if (isRecording) {
      startPoseDetection();
      startTimeRef.current = Date.now();
      dataRef.current = [];
      // Start collecting slouch data every second
      slouchIntervalRef.current = setInterval(recordSlouchData, 1000);
      setShowDownload(false);
    } else {
      stopPoseDetection();
      if (slouchIntervalRef.current) {
        clearInterval(slouchIntervalRef.current);
        slouchIntervalRef.current = null;
      }
      // Once stopped, show download button
      if (dataRef.current && dataRef.current.length > 0) {
        setShowDownload(true);
      }
    }
    // Clean up on component unmount
    return () => {
      stopPoseDetection();
      if (slouchIntervalRef.current) {
        clearInterval(slouchIntervalRef.current);
      }
      setLastToastTime(0);
    };
  }, [isRecording]);

  function handleStartStopClick() {
    setIsRecording((prevIsRecording) => !prevIsRecording);
  }

  function handleReCalibrate() {
    goodPosture = postureRef.current;
    PoseNotFound({
      text: "Posture Re-calibrated",
      subtext: "Your posture has been re-calibrated",
    });
  }

  function onResults(results) {
    if (!loaded && isRecording) {
      loaded = true;
      console.log("HPE model finished loading.");
    }

    if (!results.poseLandmarks) {
      const now = Date.now();
      if (now - lastToastTime > 2000 && isRecording) {
        PoseNotFound({
          text: "Posture Not Found",
          subtext:
            "Your posture cant be detected, please ensure you are properly visible",
        });
        if (notifPing.current) notifPing.current.play();
        setLastToastTime(now);
      }
      postureRef.current = -1; // indicate no pose
      lastSlouchStateRef.current = false;
      return;
    }

    let landmarks = results.poseLandmarks;
    postureRef.current = landmarks; // store the latest landmarks

    const canvasElement = canvasRef.current;
    const canvasCtx = canvasElement.getContext("2d");

    if (isRecording) {
      canvasCtx.save();
      canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);

      canvasCtx.globalCompositeOperation = "source-over";
      drawConnectors(canvasCtx, landmarks, mediapipePose.POSE_CONNECTIONS, {
        color: "#c6fcff",
        lineWidth: 4,
      });

      drawLandmarks(canvasCtx, landmarks, {
        color: "#c6fcff",
        lineWidth: 1,
        radius: 6,
      });

      canvasCtx.restore();

      if (!goodPosture) {
        goodPosture = landmarks;
        PoseNotFound({
          text: "Posture Calibrated",
          subtext: "Your posture has been calibrated",
        });
      } else {
        const isBad = badPosture(landmarks, goodPosture);
        lastSlouchStateRef.current = isBad;
        if (isBad) {
          badPostureCount++;
          if (badPostureCount >= 60 && isRecording) {
            PoseNotFound({
              text: "Correct your Posture",
              subtext: "Please correct your posture, if you want to live long",
            });
            if (notifPing.current) notifPing.current.play();
            badPostureCount = 0;
          }
        } else {
          badPostureCount = 0;
        }
      }
    }
  }

  function startPoseDetection() {
    const pose = new Pose({
      locateFile: (file) =>
        `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`,
    });

    pose.setOptions({
      modelComplexity: 1,
      smoothLandmarks: true,
      enableSegmentation: false,
      smoothSegmentation: false,
      minDetectionConfidence: 0.5,
      minTrackingConfidence: 0.5,
    });

    pose.onResults(onResults);
    poseRef.current = pose;

    if (webcamRef.current) {
      const camera = new cam.Camera(webcamRef.current.video, {
        onFrame: async () => {
          if (poseRef.current) {
            await poseRef.current.send({ image: webcamRef.current.video });
          }
        },
      });
      camera.start();
      cameraRef.current = camera;
    }
  }

  function stopPoseDetection() {
    if (poseRef.current) {
      poseRef.current = null; // Stop processing frames
    }

    const canvasElement = canvasRef.current;
    const canvasCtx = canvasElement.getContext("2d");
    canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
  }

  function badPosture(currLandmarks, idealLandmarks) {
    // Simple heuristic
    let lookingDown =
      currLandmarks[0].y - idealLandmarks[0].y >
      idealLandmarks[9].y - idealLandmarks[0].y;
    let faceIsClose = idealLandmarks[0].z - currLandmarks[0].z > 0.3;
    return lookingDown || faceIsClose;
  }

  function recordSlouchData() {
    // Record current time and slouch state
    const currentTime = new Date().toISOString(); // ISO8601 timestamp
    const slouch = lastSlouchStateRef.current ? 1 : 0;
    dataRef.current.push({ timestamp: currentTime, slouch });
  }

  function handleDownload() {
    const header = "timestamp,slouch\n";
    const rows = dataRef.current
      .map((d) => `${d.timestamp},${d.slouch}`)
      .join("\n");
    const csvContent = "data:text/csv;charset=utf-8," + header + rows;
    const encodedUri = encodeURI(csvContent);
    const link = document.createElement("a");
    link.setAttribute("href", encodedUri);
    link.setAttribute("download", "posture_data.csv");
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }

  return (
    <main className="fixed inset-0 w-full h-full overflow-hidden">
      <Notifications />
      <Toaster position="top-right" />
      <audio ref={notifPing} src={NotificationPing} />

      <div className="relative w-full h-full">
        <Webcam
          ref={webcamRef}
          className="absolute inset-0 w-full h-full object-cover"
          videoConstraints={{
            width: 1920,
            height: 1440,
            aspectRatio: 1920 / 1440,
          }}
          audio={false}
        />
        <canvas
          ref={canvasRef}
          className="absolute inset-0 w-full h-full"
          width={1920}
          height={1440}
        />
        <div className="absolute top-0 left-1/2 transform -translate-x-1/2 mt-4">
          <div className="text-[#c6fcff] p-4 rounded text-5xl md:text-6xl font-extrabold">
            <h1>
              We<span className="text-[#155084]">Care</span>
            </h1>
          </div>
        </div>
        <div className="flex flex-col absolute bottom-0 left-1/2 transform -translate-x-1/2 mb-8 gap-4">
          <div className="flex flex-col items-center">
            <div>
              <button
                onClick={handleReCalibrate}
                className="bg-blue-500 hover:bg-blue-600 text-white rounded-xl px-4 py-2 hover:scale-110 transition ease-in-out mb-2"
              >
                Re-Calibrate
              </button>
            </div>
            <div>
              {!isRecording ? (
                <button
                  onClick={handleStartStopClick}
                  className="bg-green-500 hover:bg-green-600 text-white rounded-xl px-4 py-2 hover:scale-110 transition ease-in-out mb-2"
                >
                  Start Recording
                </button>
              ) : (
                <button
                  onClick={handleStartStopClick}
                  className="bg-red-500 hover:bg-red-600 text-white rounded-xl px-4 py-2 hover:scale-110 transition ease-in-out"
                >
                  Stop Recording
                </button>
              )}
            </div>
            {showDownload && (
              <button
                onClick={handleDownload}
                className="bg-yellow-500 hover:bg-yellow-600 text-white rounded-xl px-4 py-2 hover:scale-110 transition ease-in-out mt-2"
              >
                Download
              </button>
            )}
          </div>
        </div>
      </div>
    </main>
  );
}
